package com.fqxiny.aop01.aspect;

import com.fqxiny.aop01.annotation.MyCacheEL;
import jakarta.annotation.Resource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Method;

@Aspect
@Component
public class ELCacheAspect {
    @Resource
    RedisTemplate redisTemplate;
    @Resource
    RedissonClient redissonClient;
    private final ExpressionParser parser = new SpelExpressionParser();
    private final StandardEvaluationContext context = new StandardEvaluationContext();
    private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new StandardReflectionParameterNameDiscoverer();

    @Around("@annotation(myCacheEL)")
    public Object doAround(ProceedingJoinPoint joinPoint, MyCacheEL myCacheEL) throws Throwable {
        // 获取方法签名和参数名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();

        // 获取参数名称数组（需要编译时保留 -parameters）
        String[] paramNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);

        // 创建 EvaluationContext 并注册参数
        StandardEvaluationContext evalContext = new StandardEvaluationContext();
        if (paramNames != null && args != null) {
            for (int i = 0; i < paramNames.length; i++) {
                evalContext.setVariable(paramNames[i], args[i]);
            }
        }

        // 解析 suffixKey 中的 SpEL 表达式（如 #id）
        String suffixKey = myCacheEL.suffixKey();
        Object suffixValue = parser.parseExpression(suffixKey).getValue(evalContext, Object.class);

        // 构造最终缓存 key
        String bussinessKey = myCacheEL.prefixKey() + suffixValue;

        // 先从缓存取值
        Object result = redisTemplate.opsForValue().get(bussinessKey);
        if(result != null && ObjectUtils.isEmpty(result)){
            return result;
        }
        // 执行原方法,从数据库取值(先用Redisson加锁)
        // 获取业务key
        String lockkey = bussinessKey+"prefix";

        RLock lock = redissonClient.getLock(lockkey);
        if(lock.tryLock()){
            try {
                result = joinPoint.proceed();
                redisTemplate.opsForValue().set(bussinessKey, result, myCacheEL.expireTimeMills(), myCacheEL.unit());
                return result;
            } catch (Throwable e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
        }else{
           return  this.doAround(joinPoint, myCacheEL);
        }

    }
}